#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "config.h"

namespace SkyID
{

Config::Config(const char *configName) :
	windowWidth(800), windowHeight(600), hideCursor(false)
{
	FILE *reader = fopen(configName, "rt");
	if (!reader)
	{
		LOG_MSG("Config: use default parameters");
		return;
	}
	parse(reader);
	fclose(reader);
}

Config::~Config()
{
	// if we need to clean some allocated memory
}

bool Config::parse(FILE *reader)
{
	LOG_ASSERT(reader);

	// open and parse config file
	char line[CONFIG_BUFFER_SIZE], param[CONFIG_BUFFER_SIZE], value[CONFIG_BUFFER_SIZE];

	// do parse
	// read line from file
	while (getLine(reader, line, CONFIG_BUFFER_SIZE))
	{
		// parse line
		if (parseLine(line, param, CONFIG_BUFFER_SIZE, value, CONFIG_BUFFER_SIZE))
		{
			LOG_MSG("Config: %s = %s", param, value);

			// save parsed value
			if (strcmp(param, "windowWidth") == 0)
				windowWidth = atoi(value);
			else if (strcmp(param, "windowHeight") == 0)
				windowHeight = atoi(value);
			else if (strcmp(param, "hideCursor") == 0)
				hideCursor = (bool)atoi(value);
			else
				LOG_MSG("Config: unknown parameter '%s'", param);
		}
	}

	LOG_MSG("Config: parse done");
	return true;
}

bool Config::getLine(FILE *reader, char *buffer, uint32 bufferLength)
{
	LOG_ASSERT(reader && buffer);

	uint32 i = 0;
	while (i < bufferLength)
		if (fread(buffer+i, 1, 1, reader)!=1 || buffer[i++]=='\n')
			break;
	buffer[i] = 0;
	return (i > 0);
}

bool Config::parseLine(const char *line, char *param, uint32 paramLength,
	char *value, uint32 valueLength)
{
	LOG_ASSERT(line && param && value);

	const char *sharpDetected = strchr(line, '#'); // detect comment
	uint32 delimiter = 0, len = sharpDetected ? sharpDetected-line : strlen(line);

	for (uint32 i=0; i<len; ++i)
	{
		if (line[i]=='=')
		{
			delimiter = i;
			break;
		}
	}

	if (!delimiter)
		return false;

	if (!trimString(line, delimiter, param, paramLength)
		|| !trimString(line+delimiter+1, len-delimiter-1, value, valueLength))
	{
		return false;
	}

	return true;
}

bool Config::trimString(const char *str, uint32 strLength, char *buffer, uint32 bufferLength)
{
	LOG_ASSERT(str && buffer);

	const char *symBegin = NULL, *symEnd = NULL;
	for (uint32 i=0; i<strLength; ++i)
	{
		if (str[i]==' ' || str[i]=='\t' || str[i]=='\r' || str[i]=='\n')
			continue;
		if (!symBegin)
			symBegin = str+i;
		symEnd = str+i;
	}

	if (symBegin > symEnd)
		return false;

	strLength = symEnd-symBegin+1;

	if (bufferLength < strLength)
		return false;

	strncpy(buffer, symBegin, strLength);
	buffer[strLength] = 0;

	return true;
}

}
